<?php

/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */

/**
 * This file contains the PDORM_Collection class.
 *
 * PHP version 5
 *
 * This file is part of PEAR::PDORM - A database object relational management
 * layer for PHP 5 built using PDO. PHP version 5.1+ is recommended.
 *
 * PEAR::PDORM is free software: you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option)
 * any later version.
 *
 * PEAR::PDORM is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with PEAR::PDORM. If not, see <http://www.gnu.org/licenses/>.
 *
 * @package     PDORM
 * @category    Object-relational Management
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 * @copyright   2007 Michael J. I. Jackson
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt GNU LGPL 3.0
 * @version     SVN: $Id: Collection.php 42 2007-12-12 04:59:11Z mjijackson $
 * @link        http://pear.php.net/packages/PDORM
 */

/**
 * A type-safe collection of domain objects. Implements several interfaces that
 * make domain object collections behave like arrays.
 *
 * <code>
 * // Create a collection to hold 'person' domain objects
 * $coll = new PDORM_Collection('person');
 *
 * // ArrayAccess
 * $coll[] = new Object_Person('Michael', 'Jackson');
 *
 * // Iterator
 * foreach ($coll as $person) {
 *     echo $person->first_name;
 * }
 *
 * // Countable
 * echo count($coll);
 * </code>
 *
 * @package     PDORM
 * @category    Object-relational Management
 * @author      Michael J. I. Jackson <mjijackson@gmail.com>
 */
class PDORM_Collection implements Iterator, ArrayAccess, Countable
{

    /**
     * This collection's internal pointer.
     *
     * @var     int
     * @access  private
     */
    private $key;

    /**
     * This collection's domain class.
     *
     * @var     string
     * @access  private
     */
    private $domain_cls;

    /**
     * The container for this collection's objects.
     *
     * @var     array
     * @access  private
     */
    private $objects = array();

    /**
     * Constructor.
     *
     * @param   string  $domain_cls     The domain class this collection
     *                                  contains objects of
     * @access  public
     */
    public function __construct($domain_cls)
    {
        $this->domain_cls = $domain_cls;
    }

    /**
     * Gets the name of the domain class this collection contains.
     *
     * @return  string          The name of this collection's domain class
     * @access  public
     */
    public function domainClass()
    {
        return $this->domain_cls;
    }

    /**
     * Gets the current element in the collection (according to the position of
     * its key).
     *
     * @return  mixed           The object at the current position
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceIterator.html Iterator
     */
    public function current()
    {
        return $this->offsetGet($this->key);
    }

    /**
     * Gets the value of this collection's key. Part of the Iterator interface.
     *
     * @return  int             The collection's key
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceIterator.html Iterator
     */
    public function key()
    {
        return $this->key;
    }

    /**
     * Gets the next element in this collection (according to the position of
     * its key).
     *
     * @return  mixed           The next element in the collection
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceIterator.html Iterator
     */
    public function next()
    {
        $next = $this->current();
        if ($next) {
            $this->key++;
        }
        return $next;
    }

    /**
     * Rewinds this collection's key to 0.
     *
     * @return  void
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceIterator.html Iterator
     */
    public function rewind()
    {
        $this->key = 0;
    }

    /**
     * Determines if the element at the current key position is valid.
     *
     * @return  bool            True if the current element is valid
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceIterator.html Iterator
     */
    public function valid()
    {
        return !is_null($this->current());
    }

    /**
     * Determines if the specified offset exists in this collection.
     *
     * @param   mixed   $offset     The offset to check
     * @return  bool                True if the specified offset exists
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html ArrayAccess
     */
    public function offsetExists($offset)
    {
        return isset($this->objects[$offset]);
    }

    /**
     * Gets the element at the specified offset in the collection.
     *
     * @param   mixed   $offset     The offset to get
     * @return  mixed               The element at the specified offset
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html ArrayAccess
     */
    public function offsetGet($offset)
    {
        return $this->offsetExists($offset) ? $this->objects[$offset] : null;
    }

    /**
     * Adds a record to this collection.
     *
     * @param   int             $offset     The offset to set
     * @param   PDORM_Record    $obj        The record to add
     * @return  void
     * @throws  PDORM_TypeException     If the given object does is not of
     *                                  the correct type for this collection
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html ArrayAccess
     */
    public function offsetSet($offset, $obj)
    {
        $obj_cls = $this->domainClass();
        if (!($obj instanceof $obj_cls)) {
            throw new PDORM_TypeException(get_class($obj), $obj_cls);
        }

        if (is_null($offset)) {
            $offset = $this->count();
        }
        $this->objects[$offset] = $obj;
    }

    /**
     * Unsets the specified offset in this collection.
     *
     * @param   mixed   $offset     The offset to unset
     * @return  void
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceArrayAccess.html ArrayAccess
     */
    public function offsetUnset($offset)
    {
        if (is_int($offset)) {
            array_splice($this->objects, $offset, 1);
        } else {
            unset($this->objects[$offset]);
        }
    }

    /**
     * Gets the number of elements in this collection.
     *
     * @return  int         The number of elements
     * @access  public
     * @link    http://www.php.net/~helly/php/ext/spl/interfaceCountable.html Countable
     */
    public function count()
    {
        return count($this->objects);
    }

}

?>
